#include "esp8266.h"

//定义全局变量
volatile uint32_t esp8266_transparent_transmission_sta = 0;

/**
 * @brief ESP-01 模块初始化
 * 
 */
int32_t esp8266_init(void)
{
	int32_t ret;

	USART3_Config(115200);

	//退出透传模式，才能输入AT指令
	ret = esp8266_exit_transparent_transmission();
	if (ret)
	{
		print_ln("esp8266_exit_transparent_transmission fail.");
		return -1;
	}
	print_ln("esp8266_exit_transparent_transmission success.");
	delay_ms(1000); //等待1s

	//复位模块
	ret = esp8266_reset();
	if (ret)
	{
		print_ln("esp8266_reset fail. ret=%d", ret);
		return -2;
	}
	print_ln("esp8266_reset success.");

	//测试AT指令
	ret = esp8266_check();
	if (ret)
	{
		print_ln("esp8266_check fail. ret=%d", ret);
		return -3;
	}
	print_ln("esp8266_check success.");
	delay_ms(1000);
	delay_ms(1000); // 等待2s

	//关闭回显
	ret = esp8266_enable_echo(0);
	if (ret)
	{
		print_ln("esp8266_enable_echo(0) fail. ret=%d", ret);
		return -4;
	}
	print_ln("esp8266_enable_echo(0)success.");
	delay_ms(1000);
	delay_ms(1000); // 等待2s

	//连接热点
	ret = esp8266_connect_ap(WIFI_SSID, WIFI_PASSWORD);
	if (ret)
	{
		print_ln("esp8266_connect_ap fail. ret=%d", ret);
		return -5;
	}
	print_ln("esp8266_connect_ap success.");
	delay_ms(1000);
	delay_ms(1000); // 等待2s

	return 0;
}

/**
 * @brief 向串口3发送字符串
 * 
 * @param buf 字符串地址
 */
void esp8266_send_str(char *buf)
{
	/*串口3发送数据*/
	usart3_send_str(buf);
}

/**
 * @brief 向串口3发送AT指令，并初始化接收缓冲区
 * 
 * @param buf 字符串地址
 */
void esp8266_send_at(char *buf)
{
	/*清空接收缓冲区*/
	memset((void *)g_usart3_buf, 0, sizeof(g_usart3_buf));

	#if WIFI_DEBUG
	print_ln("usart3 send_data: %s", buf);
	#endif

	/*串口3发送数据*/
	usart3_send_str(buf);
}

/**
 * @brief 查找接收数据包中的字符串
 * 
 * @param dest 查找目标字符串
 * @param timeout 超时时间(单位：ms)
 * @return int32_t 查到0，未查到-1
 */
int32_t esp8266_find_str_in_rx_packet(char *dest, uint32_t timeout)
{
	WHILE_TIMEOUT_MS(!g_usart3_flag, timeout)
	{
		return -2;
	}
	WHILE_TIMEOUT_MS_END

	#if WIFI_DEBUG
	print_ln("usart3 rcv_data: %s", (char *)g_usart3_buf);
	#endif

	char *src = (char *)&g_usart3_buf;
	//等待串口接收完毕或超时退出
	while ((strstr(src, dest) == NULL) && timeout)
	{
		delay_ms(1);
		timeout--;
	}
	g_usart3_flag = 0; // 重置串口3接收标志位
	if (timeout)
		return 0;
	return -1;
}

/**
 * @brief 进入透传模式
 * 
 * @return int32_t 成功0，进入透传模式失败-1，开启发送状态失败-2
 */
int32_t esp8266_entry_transparent_transmission(void)
{
	esp8266_send_at("AT+CIPMODE=1\r\n");
	if (esp8266_find_str_in_rx_packet("OK", 5000))
		return -1;
	delay_ms(1000);
	delay_ms(1000);

	//开启发送状态
	esp8266_send_at("AT+CIPSEND\r\n");
	if (esp8266_find_str_in_rx_packet("OK", 5000))
		return -2;
	delay_ms(1000);
	delay_ms(1000);

	//记录当前esp8266工作模式在透传模式
	esp8266_transparent_transmission_sta = 1;
	return 0;
}

/**
 * @brief 退出透传模式
 * 
 * @return int32_t 成功0，失败-1
 */
int32_t esp8266_exit_transparent_transmission(void)
{
	// esp8266_send_str("+++");
	esp8266_send_at("+++");

	//退出透传模式，发送下一条指令需要间隔1s
	delay_ms(1000);
	g_usart3_flag = 0;
	// 发送时会自动清空接收计数值

	//记录当前esp8266工作模式在透传模式
	esp8266_transparent_transmission_sta = 0;

	return 0;
}

/**
 * @brief 连接热点
 * 说明： 
 *      失败的原因有以下几种(UART通信和ESP8266正常情况下)
 *      1. WIFI名和密码不正确
 *      2. 路由器连接设备太多,未能给ESP8266分配IP
 * @param ssid 热点名
 * @param pswd 密码
 * @return int32_t 连接成功0，设置WIFI模式失败-1，设置多连接失败-2
 */
int32_t esp8266_connect_ap(char *ssid, char *password)
{
	char send_data_buf[128] = {0};
	esp8266_send_at("AT+CWMODE_CUR=1\r\n");
	if (esp8266_find_str_in_rx_packet("OK", 1000))
		return -1;

	//设为单连接，0单连接，1多连接
	esp8266_send_at("AT+CIPMUX=0\r\n");
	if (esp8266_find_str_in_rx_packet("OK", 1000))
		return -2;

	//连接热点
	// esp8266_send_at("AT+CWJAP_CUR=");
	// esp8266_send_at("\"");
	// esp8266_send_at(ssid);
	// esp8266_send_at("\"");
	// esp8266_send_at(",");
	// esp8266_send_at("\"");
	// esp8266_send_at(password);
	// esp8266_send_at("\"");
	// esp8266_send_at("\r\n");
	sprintf(send_data_buf, "AT+CWJAP_CUR=\"%s\",\"%s\"\r\n", ssid, password);
	esp8266_send_at(send_data_buf);

	//连接热点，务必等待该条指令返回WIFI GOT IP，表示连接成功后，再发送下面的指令；
	while (esp8266_find_str_in_rx_packet("WIFI GOT IP", 5000))
	{
		print_ln("WIFI GOT IP ERROR.");
	}
	return 0;
}

/**
 * @brief 使用指定协议(TCP/UDP)连接到服务器
 * 说明： 
 *      失败的原因有以下几种(UART通信和ESP8266正常情况下)
 *      1. 远程服务器IP和端口号有误
 *      2. 未连接AP
 *      3. 服务器端禁止添加(一般不会发生)
 * 
 * @param mode TCP或UDP
 * @param ip 目标IP地址
 * @param port 目标端口号
 * @return int32_t 成功0，失败-1
 */
int32_t esp8266_connect_server(char *mode, char *ip, uint16_t port)
{
	char buf[8];
	sprintf(buf, "%d", port);
	esp8266_send_at("AT+CIPSTART=");
	esp8266_send_at("\"");
	esp8266_send_at(mode);
	esp8266_send_at("\"");
	esp8266_send_at(",");
	esp8266_send_at("\"");
	esp8266_send_at(ip);
	esp8266_send_at("\"");
	esp8266_send_at(",");
	esp8266_send_at(buf);
	esp8266_send_at("\r\n");

	//等待响应 CONNECT OK
	if (esp8266_find_str_in_rx_packet("CONNECT", 5000))
		if (esp8266_find_str_in_rx_packet("OK", 5000))
			return -1;
	return 0;
}

/**
 * @brief 断开服务器的连接
 * 
 * @return int32_t 成功0，失败-1
 */
int32_t esp8266_disconnect_server(void)
{
	esp8266_send_at("AT+CIPCLOSE\r\n");

	if (esp8266_find_str_in_rx_packet("CLOSED", 2000))
		if (esp8266_find_str_in_rx_packet("OK", 2000))
			return -1;

	return 0;
}

/**
 * @brief 检查网络服务器连接状态
 * 
 * @return int32_t 0:已建⽴TCP或UDP传输
 * 				   -1:已连接 AP，获得 IP 地址
 *    			   或:断开⽹络连接 
 * 	      	       或:未连接 AP
 */
int32_t esp8266_check_connection_status(void)
{
	esp8266_send_at("AT+CIPSTATUS\r\n");
	if (esp8266_find_str_in_rx_packet("STATUS:3", 10000))
		if (esp8266_find_str_in_rx_packet("OK", 10000))
			return -1;

	return 0;
}

/**
 * @brief 回显打开或关闭
 * 
 * @param b 0:关闭 1:打开
 * @return int32_t 0:成功 -1：失败
 */
int32_t esp8266_enable_echo(uint32_t b)
{
	if (b)
		esp8266_send_at("ATE1\r\n");
	else
		esp8266_send_at("ATE0\r\n");

	if (esp8266_find_str_in_rx_packet("OK", 5000))
		return -1;

	return 0;
}

/**
 * @brief 复位ESP8266
 * 
 * @return int32_t 0:成功 -1:失败
 */
int32_t esp8266_reset(void)
{
	esp8266_send_at("AT+RST\r\n");
	if (esp8266_find_str_in_rx_packet("OK", 10000))
	{
		g_usart3_flag = 0;
		return -1;
	}
	delay_ms(1000);
	delay_ms(1000); // 等待2s
	g_usart3_flag = 0;
	return 0;
}

/**
 * @brief 检查ESP8266是否正常 
 * 
 * @return int32_t 0：正常 -1：不正常
 */
int32_t esp8266_check(void)
{
	esp8266_send_at("AT\r\n");
	if (esp8266_find_str_in_rx_packet("OK", 10000))
		return -1;

	return 0;
}
